1 /* 2 * Copyright (c) 2000, 2008, Oracle and/or its affiliates. All rights reserved. 3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 4 * 5 * This code is free software; you can redistribute it and/or modify it 6 * under the terms of the GNU General Public License version 2 only, as 7 * published by the Free Software Foundation. Oracle designates this 8 * particular file as subject to the "Classpath" exception as provided 9 * by Oracle in the LICENSE file that accompanied this code. 10 * 11 * This code is distributed in the hope that it will be useful, but WITHOUT 12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 14 * version 2 for more details (a copy is included in the LICENSE file that 15 * accompanied this code). 16 * 17 * You should have received a copy of the GNU General Public License version 18 * 2 along with this work; if not, write to the Free Software Foundation, 19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 20 * 21 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 22 * or visit www.oracle.com if you need additional information or have any 23 * questions. 24 */ 25 26 package javax.management.openmbean; 27 28 import java.io.ObjectStreamException; 29 import java.lang.reflect.Array; 30 31 /** 32 * The <code>ArrayType</code> class is the <i>open type</i> class whose instances describe 33 * all <i>open data</i> values which are n-dimensional arrays of <i>open data</i> values. 34 * <p> 35 * Examples of valid {@code ArrayType} instances are: 36 * <pre> 37 * // 2-dimension array of java.lang.String 38 * ArrayType<String[][]> a1 = new ArrayType<String[][]>(2, SimpleType.STRING); 39 * 40 * // 1-dimension array of int 41 * ArrayType<int[]> a2 = new ArrayType<int[]>(SimpleType.INTEGER, true); 42 * 43 * // 1-dimension array of java.lang.Integer 44 * ArrayType<Integer[]> a3 = new ArrayType<Integer[]>(SimpleType.INTEGER, false); 45 * 46 * // 4-dimension array of int 47 * ArrayType<int[][][][]> a4 = new ArrayType<int[][][][]>(3, a2); 48 * 49 * // 4-dimension array of java.lang.Integer 50 * ArrayType<Integer[][][][]> a5 = new ArrayType<Integer[][][][]>(3, a3); 51 * 52 * // 1-dimension array of java.lang.String 53 * ArrayType<String[]> a6 = new ArrayType<String[]>(SimpleType.STRING, false); 54 * 55 * // 1-dimension array of long 56 * ArrayType<long[]> a7 = new ArrayType<long[]>(SimpleType.LONG, true); 57 * 58 * // 1-dimension array of java.lang.Integer 59 * ArrayType<Integer[]> a8 = ArrayType.getArrayType(SimpleType.INTEGER); 60 * 61 * // 2-dimension array of java.lang.Integer 62 * ArrayType<Integer[][]> a9 = ArrayType.getArrayType(a8); 63 * 64 * // 2-dimension array of int 65 * ArrayType<int[][]> a10 = ArrayType.getPrimitiveArrayType(int[][].class); 66 * 67 * // 3-dimension array of int 68 * ArrayType<int[][][]> a11 = ArrayType.getArrayType(a10); 69 * 70 * // 1-dimension array of float 71 * ArrayType<float[]> a12 = ArrayType.getPrimitiveArrayType(float[].class); 72 * 73 * // 2-dimension array of float 74 * ArrayType<float[][]> a13 = ArrayType.getArrayType(a12); 75 * 76 * // 1-dimension array of javax.management.ObjectName 77 * ArrayType<ObjectName[]> a14 = ArrayType.getArrayType(SimpleType.OBJECTNAME); 78 * 79 * // 2-dimension array of javax.management.ObjectName 80 * ArrayType<ObjectName[][]> a15 = ArrayType.getArrayType(a14); 81 * 82 * // 3-dimension array of java.lang.String 83 * ArrayType<String[][][]> a16 = new ArrayType<String[][][]>(3, SimpleType.STRING); 84 * 85 * // 1-dimension array of java.lang.String 86 * ArrayType<String[]> a17 = new ArrayType<String[]>(1, SimpleType.STRING); 87 * 88 * // 2-dimension array of java.lang.String 89 * ArrayType<String[][]> a18 = new ArrayType<String[][]>(1, a17); 90 * 91 * // 3-dimension array of java.lang.String 92 * ArrayType<String[][][]> a19 = new ArrayType<String[][][]>(1, a18); 93 * </pre> 94 * 95 * 96 * @since 1.5 97 */ 98 /* 99 Generification note: we could have defined a type parameter that is the 100 element type, with class ArrayType<E> extends OpenType<E[]>. However, 101 that doesn't buy us all that much. We can't say 102 public OpenType<E> getElementOpenType() 103 because this ArrayType could be a multi-dimensional array. 104 For example, if we had 105 ArrayType(2, SimpleType.INTEGER) 106 then E would have to be Integer[], while getElementOpenType() would 107 return SimpleType.INTEGER, which is an OpenType<Integer>. 108 109 Furthermore, we would like to support int[] (as well as Integer[]) as 110 an Open Type (RFE 5045358). We would want this to be an OpenType<int[]> 111 which can't be expressed as <E[]> because E can't be a primitive type 112 like int. 113 */ 114 public class ArrayType<T> extends OpenType<T> { 115 116 /* Serial version */ 117 static final long serialVersionUID = 720504429830309770L; 118 119 /** 120 * @serial The dimension of arrays described by this {@link ArrayType} 121 * instance. 122 */ 123 private int dimension; 124 125 /** 126 * @serial The <i>open type</i> of element values contained in the arrays 127 * described by this {@link ArrayType} instance. 128 */ 129 private OpenType<?> elementType; 130 131 /** 132 * @serial This flag indicates whether this {@link ArrayType} 133 * describes a primitive array. 134 * 135 * @since 1.6 136 */ 137 private boolean primitiveArray; 138 139 private transient Integer myHashCode = null; // As this instance is immutable, these two values 140 private transient String myToString = null; // need only be calculated once. 141 142 // indexes refering to columns in the PRIMITIVE_ARRAY_TYPES table. 143 private static final int PRIMITIVE_WRAPPER_NAME_INDEX = 0; 144 private static final int PRIMITIVE_TYPE_NAME_INDEX = 1; 145 private static final int PRIMITIVE_TYPE_KEY_INDEX = 2; 146 private static final int PRIMITIVE_OPEN_TYPE_INDEX = 3; 147 148 private static final Object[][] PRIMITIVE_ARRAY_TYPES = { 149 { Boolean.class.getName(), boolean.class.getName(), "Z", SimpleType.BOOLEAN }, 150 { Character.class.getName(), char.class.getName(), "C", SimpleType.CHARACTER }, 151 { Byte.class.getName(), byte.class.getName(), "B", SimpleType.BYTE }, 152 { Short.class.getName(), short.class.getName(), "S", SimpleType.SHORT }, 153 { Integer.class.getName(), int.class.getName(), "I", SimpleType.INTEGER }, 154 { Long.class.getName(), long.class.getName(), "J", SimpleType.LONG }, 155 { Float.class.getName(), float.class.getName(), "F", SimpleType.FLOAT }, 156 { Double.class.getName(), double.class.getName(), "D", SimpleType.DOUBLE } 157 }; 158 159 static boolean isPrimitiveContentType(final String primitiveKey) { 160 for (Object[] typeDescr : PRIMITIVE_ARRAY_TYPES) { 161 if (typeDescr[PRIMITIVE_TYPE_KEY_INDEX].equals(primitiveKey)) { 162 return true; 163 } 164 } 165 return false; 166 } 167 168 /** 169 * Return the key used to identify the element type in 170 * arrays - e.g. "Z" for boolean, "C" for char etc... 171 * @param elementClassName the wrapper class name of the array 172 * element ("Boolean", "Character", etc...) 173 * @return the key corresponding to the given type ("Z", "C", etc...) 174 * return null if the given elementClassName is not a primitive 175 * wrapper class name. 176 **/ 177 static String getPrimitiveTypeKey(String elementClassName) { 178 for (Object[] typeDescr : PRIMITIVE_ARRAY_TYPES) { 179 if (elementClassName.equals(typeDescr[PRIMITIVE_WRAPPER_NAME_INDEX])) 180 return (String)typeDescr[PRIMITIVE_TYPE_KEY_INDEX]; 181 } 182 return null; 183 } 184 185 /** 186 * Return the primitive type name corresponding to the given wrapper class. 187 * e.g. "boolean" for "Boolean", "char" for "Character" etc... 188 * @param elementClassName the type of the array element ("Boolean", 189 * "Character", etc...) 190 * @return the primitive type name corresponding to the given wrapper class 191 * ("boolean", "char", etc...) 192 * return null if the given elementClassName is not a primitive 193 * wrapper type name. 194 **/ 195 static String getPrimitiveTypeName(String elementClassName) { 196 for (Object[] typeDescr : PRIMITIVE_ARRAY_TYPES) { 197 if (elementClassName.equals(typeDescr[PRIMITIVE_WRAPPER_NAME_INDEX])) 198 return (String)typeDescr[PRIMITIVE_TYPE_NAME_INDEX]; 199 } 200 return null; 201 } 202 203 /** 204 * Return the primitive open type corresponding to the given primitive type. 205 * e.g. SimpleType.BOOLEAN for "boolean", SimpleType.CHARACTER for 206 * "char", etc... 207 * @param primitiveTypeName the primitive type of the array element ("boolean", 208 * "char", etc...) 209 * @return the OpenType corresponding to the given primitive type name 210 * (SimpleType.BOOLEAN, SimpleType.CHARACTER, etc...) 211 * return null if the given elementClassName is not a primitive 212 * type name. 213 **/ 214 static SimpleType<?> getPrimitiveOpenType(String primitiveTypeName) { 215 for (Object[] typeDescr : PRIMITIVE_ARRAY_TYPES) { 216 if (primitiveTypeName.equals(typeDescr[PRIMITIVE_TYPE_NAME_INDEX])) 217 return (SimpleType<?>)typeDescr[PRIMITIVE_OPEN_TYPE_INDEX]; 218 } 219 return null; 220 } 221 222 /* *** Constructor *** */ 223 224 /** 225 * Constructs an <tt>ArrayType</tt> instance describing <i>open data</i> values which are 226 * arrays with dimension <var>dimension</var> of elements whose <i>open type</i> is <var>elementType</var>. 227 * <p> 228 * When invoked on an <tt>ArrayType</tt> instance, the {@link OpenType#getClassName() getClassName} method 229 * returns the class name of the array instances it describes (following the rules defined by the 230 * {@link Class#getName() getName} method of <code>java.lang.Class</code>), not the class name of the array elements 231 * (which is returned by a call to <tt>getElementOpenType().getClassName()</tt>). 232 * <p> 233 * The internal field corresponding to the type name of this <code>ArrayType</code> instance is also set to 234 * the class name of the array instances it describes. 235 * In other words, the methods <code>getClassName</code> and <code>getTypeName</code> return the same string value. 236 * The internal field corresponding to the description of this <code>ArrayType</code> instance is set to a string value 237 * which follows the following template: 238 * <ul> 239 * <li>if non-primitive array: <tt><i><dimension></i>-dimension array of <i><element_class_name></i></tt></li> 240 * <li>if primitive array: <tt><i><dimension></i>-dimension array of <i><primitive_type_of_the_element_class_name></i></tt></li> 241 * </ul> 242 * <p> 243 * As an example, the following piece of code: 244 * <pre> 245 * ArrayType<String[][][]> t = new ArrayType<String[][][]>(3, SimpleType.STRING); 246 * System.out.println("array class name = " + t.getClassName()); 247 * System.out.println("element class name = " + t.getElementOpenType().getClassName()); 248 * System.out.println("array type name = " + t.getTypeName()); 249 * System.out.println("array type description = " + t.getDescription()); 250 * </pre> 251 * would produce the following output: 252 * <pre> 253 * array class name = [[[Ljava.lang.String; 254 * element class name = java.lang.String 255 * array type name = [[[Ljava.lang.String; 256 * array type description = 3-dimension array of java.lang.String 257 * </pre> 258 * And the following piece of code which is equivalent to the one listed 259 * above would also produce the same output: 260 * <pre> 261 * ArrayType<String[]> t1 = new ArrayType<String[]>(1, SimpleType.STRING); 262 * ArrayType<String[][]> t2 = new ArrayType<String[][]>(1, t1); 263 * ArrayType<String[][][]> t3 = new ArrayType<String[][][]>(1, t2); 264 * System.out.println("array class name = " + t3.getClassName()); 265 * System.out.println("element class name = " + t3.getElementOpenType().getClassName()); 266 * System.out.println("array type name = " + t3.getTypeName()); 267 * System.out.println("array type description = " + t3.getDescription()); 268 * </pre> 269 * 270 * @param dimension the dimension of arrays described by this <tt>ArrayType</tt> instance; 271 * must be greater than or equal to 1. 272 * 273 * @param elementType the <i>open type</i> of element values contained 274 * in the arrays described by this <tt>ArrayType</tt> 275 * instance; must be an instance of either 276 * <tt>SimpleType</tt>, <tt>CompositeType</tt>, 277 * <tt>TabularType</tt> or another <tt>ArrayType</tt> 278 * with a <tt>SimpleType</tt>, <tt>CompositeType</tt> 279 * or <tt>TabularType</tt> as its <tt>elementType</tt>. 280 * 281 * @throws IllegalArgumentException if {@code dimension} is not a positive 282 * integer. 283 * @throws OpenDataException if <var>elementType's className</var> is not 284 * one of the allowed Java class names for open 285 * data. 286 */ 287 public ArrayType(int dimension, 288 OpenType<?> elementType) throws OpenDataException { 289 // Check and construct state defined by parent. 290 // We can't use the package-private OpenType constructor because 291 // we don't know if the elementType parameter is sane. 292 super(buildArrayClassName(dimension, elementType), 293 buildArrayClassName(dimension, elementType), 294 buildArrayDescription(dimension, elementType)); 295 296 // Check and construct state specific to ArrayType 297 // 298 if (elementType.isArray()) { 299 ArrayType<?> at = (ArrayType<?>) elementType; 300 this.dimension = at.getDimension() + dimension; 301 this.elementType = at.getElementOpenType(); 302 this.primitiveArray = at.isPrimitiveArray(); 303 } else { 304 this.dimension = dimension; 305 this.elementType = elementType; 306 this.primitiveArray = false; 307 } 308 } 309 310 /** 311 * Constructs a unidimensional {@code ArrayType} instance for the 312 * supplied {@code SimpleType}. 313 * <p> 314 * This constructor supports the creation of arrays of primitive 315 * types when {@code primitiveArray} is {@code true}. 316 * <p> 317 * For primitive arrays the {@link #getElementOpenType()} method 318 * returns the {@link SimpleType} corresponding to the wrapper 319 * type of the primitive type of the array. 320 * <p> 321 * When invoked on an <tt>ArrayType</tt> instance, the {@link OpenType#getClassName() getClassName} method 322 * returns the class name of the array instances it describes (following the rules defined by the 323 * {@link Class#getName() getName} method of <code>java.lang.Class</code>), not the class name of the array elements 324 * (which is returned by a call to <tt>getElementOpenType().getClassName()</tt>). 325 * <p> 326 * The internal field corresponding to the type name of this <code>ArrayType</code> instance is also set to 327 * the class name of the array instances it describes. 328 * In other words, the methods <code>getClassName</code> and <code>getTypeName</code> return the same string value. 329 * The internal field corresponding to the description of this <code>ArrayType</code> instance is set to a string value 330 * which follows the following template: 331 * <ul> 332 * <li>if non-primitive array: <tt>1-dimension array of <i><element_class_name></i></tt></li> 333 * <li>if primitive array: <tt>1-dimension array of <i><primitive_type_of_the_element_class_name></i></tt></li> 334 * </ul> 335 * <p> 336 * As an example, the following piece of code: 337 * <pre> 338 * ArrayType<int[]> t = new ArrayType<int[]>(SimpleType.INTEGER, true); 339 * System.out.println("array class name = " + t.getClassName()); 340 * System.out.println("element class name = " + t.getElementOpenType().getClassName()); 341 * System.out.println("array type name = " + t.getTypeName()); 342 * System.out.println("array type description = " + t.getDescription()); 343 * </pre> 344 * would produce the following output: 345 * <pre> 346 * array class name = [I 347 * element class name = java.lang.Integer 348 * array type name = [I 349 * array type description = 1-dimension array of int 350 * </pre> 351 * 352 * @param elementType the {@code SimpleType} of the element values 353 * contained in the arrays described by this 354 * {@code ArrayType} instance. 355 * 356 * @param primitiveArray {@code true} when this array describes 357 * primitive arrays. 358 * 359 * @throws IllegalArgumentException if {@code dimension} is not a positive 360 * integer. 361 * @throws OpenDataException if {@code primitiveArray} is {@code true} and 362 * {@code elementType} is not a valid {@code SimpleType} for a primitive 363 * type. 364 * 365 * @since 1.6 366 */ 367 public ArrayType(SimpleType<?> elementType, 368 boolean primitiveArray) throws OpenDataException { 369 370 // Check and construct state defined by parent. 371 // We can call the package-private OpenType constructor because the 372 // set of SimpleTypes is fixed and SimpleType can't be subclassed. 373 super(buildArrayClassName(1, elementType, primitiveArray), 374 buildArrayClassName(1, elementType, primitiveArray), 375 buildArrayDescription(1, elementType, primitiveArray), 376 true); 377 378 // Check and construct state specific to ArrayType 379 // 380 this.dimension = 1; 381 this.elementType = elementType; 382 this.primitiveArray = primitiveArray; 383 } 384 385 /* Package-private constructor for callers we trust to get it right. */ 386 ArrayType(String className, String typeName, String description, 387 int dimension, OpenType<?> elementType, 388 boolean primitiveArray) { 389 super(className, typeName, description, true); 390 this.dimension = dimension; 391 this.elementType = elementType; 392 this.primitiveArray = primitiveArray; 393 } 394 395 private static String buildArrayClassName(int dimension, 396 OpenType<?> elementType) 397 throws OpenDataException { 398 boolean isPrimitiveArray = false; 399 if (elementType.isArray()) { 400 isPrimitiveArray = ((ArrayType<?>) elementType).isPrimitiveArray(); 401 } 402 return buildArrayClassName(dimension, elementType, isPrimitiveArray); 403 } 404 405 private static String buildArrayClassName(int dimension, 406 OpenType<?> elementType, 407 boolean isPrimitiveArray) 408 throws OpenDataException { 409 if (dimension < 1) { 410 throw new IllegalArgumentException( 411 "Value of argument dimension must be greater than 0"); 412 } 413 StringBuilder result = new StringBuilder(); 414 String elementClassName = elementType.getClassName(); 415 // Add N (= dimension) additional '[' characters to the existing array 416 for (int i = 1; i <= dimension; i++) { 417 result.append('['); 418 } 419 if (elementType.isArray()) { 420 result.append(elementClassName); 421 } else { 422 if (isPrimitiveArray) { 423 final String key = getPrimitiveTypeKey(elementClassName); 424 // Ideally we should throw an IllegalArgumentException here, 425 // but for compatibility reasons we throw an OpenDataException. 426 // (used to be thrown by OpenType() constructor). 427 // 428 if (key == null) 429 throw new OpenDataException("Element type is not primitive: " 430 + elementClassName); 431 result.append(key); 432 } else { 433 result.append("L"); 434 result.append(elementClassName); 435 result.append(';'); 436 } 437 } 438 return result.toString(); 439 } 440 441 private static String buildArrayDescription(int dimension, 442 OpenType<?> elementType) 443 throws OpenDataException { 444 boolean isPrimitiveArray = false; 445 if (elementType.isArray()) { 446 isPrimitiveArray = ((ArrayType<?>) elementType).isPrimitiveArray(); 447 } 448 return buildArrayDescription(dimension, elementType, isPrimitiveArray); 449 } 450 451 private static String buildArrayDescription(int dimension, 452 OpenType<?> elementType, 453 boolean isPrimitiveArray) 454 throws OpenDataException { 455 if (elementType.isArray()) { 456 ArrayType<?> at = (ArrayType<?>) elementType; 457 dimension += at.getDimension(); 458 elementType = at.getElementOpenType(); 459 isPrimitiveArray = at.isPrimitiveArray(); 460 } 461 StringBuilder result = 462 new StringBuilder(dimension + "-dimension array of "); 463 final String elementClassName = elementType.getClassName(); 464 if (isPrimitiveArray) { 465 // Convert from wrapper type to primitive type 466 final String primitiveType = 467 getPrimitiveTypeName(elementClassName); 468 469 // Ideally we should throw an IllegalArgumentException here, 470 // but for compatibility reasons we throw an OpenDataException. 471 // (used to be thrown by OpenType() constructor). 472 // 473 if (primitiveType == null) 474 throw new OpenDataException("Element is not a primitive type: "+ 475 elementClassName); 476 result.append(primitiveType); 477 } else { 478 result.append(elementClassName); 479 } 480 return result.toString(); 481 } 482 483 /* *** ArrayType specific information methods *** */ 484 485 /** 486 * Returns the dimension of arrays described by this <tt>ArrayType</tt> instance. 487 * 488 * @return the dimension. 489 */ 490 public int getDimension() { 491 492 return dimension; 493 } 494 495 /** 496 * Returns the <i>open type</i> of element values contained in the arrays described by this <tt>ArrayType</tt> instance. 497 * 498 * @return the element type. 499 */ 500 public OpenType<?> getElementOpenType() { 501 502 return elementType; 503 } 504 505 /** 506 * Returns <code>true</code> if the open data values this open 507 * type describes are primitive arrays, <code>false</code> otherwise. 508 * 509 * @return true if this is a primitive array type. 510 * 511 * @since 1.6 512 */ 513 public boolean isPrimitiveArray() { 514 515 return primitiveArray; 516 } 517 518 /** 519 * Tests whether <var>obj</var> is a value for this <code>ArrayType</code> 520 * instance. 521 * <p> 522 * This method returns <code>true</code> if and only if <var>obj</var> 523 * is not null, <var>obj</var> is an array and any one of the following 524 * is <tt>true</tt>: 525 * 526 * <ul> 527 * <li>if this <code>ArrayType</code> instance describes an array of 528 * <tt>SimpleType</tt> elements or their corresponding primitive types, 529 * <var>obj</var>'s class name is the same as the className field defined 530 * for this <code>ArrayType</code> instance (i.e. the class name returned 531 * by the {@link OpenType#getClassName() getClassName} method, which 532 * includes the dimension information),<br> </li> 533 * <li>if this <code>ArrayType</code> instance describes an array of 534 * classes implementing the {@code TabularData} interface or the 535 * {@code CompositeData} interface, <var>obj</var> is assignable to 536 * such a declared array, and each element contained in <var>obj</var> 537 * is either null or a valid value for the element's open type specified 538 * by this <code>ArrayType</code> instance.</li> 539 * </ul> 540 * 541 * @param obj the object to be tested. 542 * 543 * @return <code>true</code> if <var>obj</var> is a value for this 544 * <code>ArrayType</code> instance. 545 */ 546 public boolean isValue(Object obj) { 547 548 // if obj is null, return false 549 // 550 if (obj == null) { 551 return false; 552 } 553 554 Class<?> objClass = obj.getClass(); 555 String objClassName = objClass.getName(); 556 557 // if obj is not an array, return false 558 // 559 if ( ! objClass.isArray() ) { 560 return false; 561 } 562 563 // Test if obj's class name is the same as for the array values that this instance describes 564 // (this is fine if elements are of simple types, which are final classes) 565 // 566 if ( this.getClassName().equals(objClassName) ) { 567 return true; 568 } 569 570 // In case this ArrayType instance describes an array of classes implementing the TabularData or CompositeData interface, 571 // we first check for the assignability of obj to such an array of TabularData or CompositeData, 572 // which ensures that: 573 // . obj is of the the same dimension as this ArrayType instance, 574 // . it is declared as an array of elements which are either all TabularData or all CompositeData. 575 // 576 // If the assignment check is positive, 577 // then we have to check that each element in obj is of the same TabularType or CompositeType 578 // as the one described by this ArrayType instance. 579 // 580 // [About assignment check, note that the call below returns true: ] 581 // [Class.forName("[Lpackage.CompositeData;").isAssignableFrom(Class.forName("[Lpackage.CompositeDataImpl;)")); ] 582 // 583 if ( (this.elementType.getClassName().equals(TabularData.class.getName())) || 584 (this.elementType.getClassName().equals(CompositeData.class.getName())) ) { 585 586 boolean isTabular = 587 (elementType.getClassName().equals(TabularData.class.getName())); 588 int[] dims = new int[getDimension()]; 589 Class<?> elementClass = isTabular ? TabularData.class : CompositeData.class; 590 Class<?> targetClass = Array.newInstance(elementClass, dims).getClass(); 591 592 // assignment check: return false if negative 593 if ( ! targetClass.isAssignableFrom(objClass) ) { 594 return false; 595 } 596 597 // check that all elements in obj are valid values for this ArrayType 598 if ( ! checkElementsType( (Object[]) obj, this.dimension) ) { // we know obj's dimension is this.dimension 599 return false; 600 } 601 602 return true; 603 } 604 605 // if previous tests did not return, then obj is not a value for this ArrayType instance 606 return false; 607 } 608 609 /** 610 * Returns true if and only if all elements contained in the array argument x_dim_Array of dimension dim 611 * are valid values (ie either null or of the right openType) 612 * for the element open type specified by this ArrayType instance. 613 * 614 * This method's implementation uses recursion to go down the dimensions of the array argument. 615 */ 616 private boolean checkElementsType(Object[] x_dim_Array, int dim) { 617 618 // if the elements of x_dim_Array are themselves array: go down recursively.... 619 if ( dim > 1 ) { 620 for (int i=0; i<x_dim_Array.length; i++) { 621 if ( ! checkElementsType((Object[])x_dim_Array[i], dim-1) ) { 622 return false; 623 } 624 } 625 return true; 626 } 627 // ...else, for a non-empty array, each element must be a valid value: either null or of the right openType 628 else { 629 for (int i=0; i<x_dim_Array.length; i++) { 630 if ( (x_dim_Array[i] != null) && (! this.getElementOpenType().isValue(x_dim_Array[i])) ) { 631 return false; 632 } 633 } 634 return true; 635 } 636 } 637 638 @Override 639 boolean isAssignableFrom(OpenType<?> ot) { 640 if (!(ot instanceof ArrayType<?>)) 641 return false; 642 ArrayType<?> at = (ArrayType<?>) ot; 643 return (at.getDimension() == getDimension() && 644 at.isPrimitiveArray() == isPrimitiveArray() && 645 at.getElementOpenType().isAssignableFrom(getElementOpenType())); 646 } 647 648 649 /* *** Methods overriden from class Object *** */ 650 651 /** 652 * Compares the specified <code>obj</code> parameter with this 653 * <code>ArrayType</code> instance for equality. 654 * <p> 655 * Two <code>ArrayType</code> instances are equal if and only if they 656 * describe array instances which have the same dimension, elements' 657 * open type and primitive array flag. 658 * 659 * @param obj the object to be compared for equality with this 660 * <code>ArrayType</code> instance; if <var>obj</var> 661 * is <code>null</code> or is not an instance of the 662 * class <code>ArrayType</code> this method returns 663 * <code>false</code>. 664 * 665 * @return <code>true</code> if the specified object is equal to 666 * this <code>ArrayType</code> instance. 667 */ 668 public boolean equals(Object obj) { 669 670 // if obj is null, return false 671 // 672 if (obj == null) { 673 return false; 674 } 675 676 // if obj is not an ArrayType, return false 677 // 678 if (!(obj instanceof ArrayType<?>)) 679 return false; 680 ArrayType<?> other = (ArrayType<?>) obj; 681 682 // if other's dimension is different than this instance's, return false 683 // 684 if (this.dimension != other.dimension) { 685 return false; 686 } 687 688 // Test if other's elementType field is the same as for this instance 689 // 690 if (!this.elementType.equals(other.elementType)) { 691 return false; 692 } 693 694 // Test if other's primitiveArray flag is the same as for this instance 695 // 696 return this.primitiveArray == other.primitiveArray; 697 } 698 699 /** 700 * Returns the hash code value for this <code>ArrayType</code> instance. 701 * <p> 702 * The hash code of an <code>ArrayType</code> instance is the sum of the 703 * hash codes of all the elements of information used in <code>equals</code> 704 * comparisons (i.e. dimension, elements' open type and primitive array flag). 705 * The hashcode for a primitive value is the hashcode of the corresponding boxed 706 * object (e.g. the hashcode for <tt>true</tt> is <tt>Boolean.TRUE.hashCode()</tt>). 707 * This ensures that <code> t1.equals(t2) </code> implies that 708 * <code> t1.hashCode()==t2.hashCode() </code> for any two 709 * <code>ArrayType</code> instances <code>t1</code> and <code>t2</code>, 710 * as required by the general contract of the method 711 * {@link Object#hashCode() Object.hashCode()}. 712 * <p> 713 * As <code>ArrayType</code> instances are immutable, the hash 714 * code for this instance is calculated once, on the first call 715 * to <code>hashCode</code>, and then the same value is returned 716 * for subsequent calls. 717 * 718 * @return the hash code value for this <code>ArrayType</code> instance 719 */ 720 public int hashCode() { 721 722 // Calculate the hash code value if it has not yet been done (ie 1st call to hashCode()) 723 // 724 if (myHashCode == null) { 725 int value = 0; 726 value += dimension; 727 value += elementType.hashCode(); 728 value += Boolean.valueOf(primitiveArray).hashCode(); 729 myHashCode = Integer.valueOf(value); 730 } 731 732 // return always the same hash code for this instance (immutable) 733 // 734 return myHashCode.intValue(); 735 } 736 737 /** 738 * Returns a string representation of this <code>ArrayType</code> instance. 739 * <p> 740 * The string representation consists of the name of this class (i.e. 741 * <code>javax.management.openmbean.ArrayType</code>), the type name, 742 * the dimension, the elements' open type and the primitive array flag 743 * defined for this instance. 744 * <p> 745 * As <code>ArrayType</code> instances are immutable, the 746 * string representation for this instance is calculated 747 * once, on the first call to <code>toString</code>, and 748 * then the same value is returned for subsequent calls. 749 * 750 * @return a string representation of this <code>ArrayType</code> instance 751 */ 752 public String toString() { 753 754 // Calculate the string representation if it has not yet been done (ie 1st call to toString()) 755 // 756 if (myToString == null) { 757 myToString = getClass().getName() + 758 "(name=" + getTypeName() + 759 ",dimension=" + dimension + 760 ",elementType=" + elementType + 761 ",primitiveArray=" + primitiveArray + ")"; 762 } 763 764 // return always the same string representation for this instance (immutable) 765 // 766 return myToString; 767 } 768 769 /** 770 * Create an {@code ArrayType} instance in a type-safe manner. 771 * <p> 772 * Multidimensional arrays can be built up by calling this method as many 773 * times as necessary. 774 * <p> 775 * Calling this method twice with the same parameters may return the same 776 * object or two equal but not identical objects. 777 * <p> 778 * As an example, the following piece of code: 779 * <pre> 780 * ArrayType<String[]> t1 = ArrayType.getArrayType(SimpleType.STRING); 781 * ArrayType<String[][]> t2 = ArrayType.getArrayType(t1); 782 * ArrayType<String[][][]> t3 = ArrayType.getArrayType(t2); 783 * System.out.println("array class name = " + t3.getClassName()); 784 * System.out.println("element class name = " + t3.getElementOpenType().getClassName()); 785 * System.out.println("array type name = " + t3.getTypeName()); 786 * System.out.println("array type description = " + t3.getDescription()); 787 * </pre> 788 * would produce the following output: 789 * <pre> 790 * array class name = [[[Ljava.lang.String; 791 * element class name = java.lang.String 792 * array type name = [[[Ljava.lang.String; 793 * array type description = 3-dimension array of java.lang.String 794 * </pre> 795 * 796 * @param elementType the <i>open type</i> of element values contained 797 * in the arrays described by this <tt>ArrayType</tt> 798 * instance; must be an instance of either 799 * <tt>SimpleType</tt>, <tt>CompositeType</tt>, 800 * <tt>TabularType</tt> or another <tt>ArrayType</tt> 801 * with a <tt>SimpleType</tt>, <tt>CompositeType</tt> 802 * or <tt>TabularType</tt> as its <tt>elementType</tt>. 803 * 804 * @throws OpenDataException if <var>elementType's className</var> is not 805 * one of the allowed Java class names for open 806 * data. 807 * 808 * @since 1.6 809 */ 810 public static <E> ArrayType<E[]> getArrayType(OpenType<E> elementType) 811 throws OpenDataException { 812 return new ArrayType<E[]>(1, elementType); 813 } 814 815 /** 816 * Create an {@code ArrayType} instance in a type-safe manner. 817 * <p> 818 * Calling this method twice with the same parameters may return the 819 * same object or two equal but not identical objects. 820 * <p> 821 * As an example, the following piece of code: 822 * <pre> 823 * ArrayType<int[][][]> t = ArrayType.getPrimitiveArrayType(int[][][].class); 824 * System.out.println("array class name = " + t.getClassName()); 825 * System.out.println("element class name = " + t.getElementOpenType().getClassName()); 826 * System.out.println("array type name = " + t.getTypeName()); 827 * System.out.println("array type description = " + t.getDescription()); 828 * </pre> 829 * would produce the following output: 830 * <pre> 831 * array class name = [[[I 832 * element class name = java.lang.Integer 833 * array type name = [[[I 834 * array type description = 3-dimension array of int 835 * </pre> 836 * 837 * @param arrayClass a primitive array class such as {@code int[].class}, 838 * {@code boolean[][].class}, etc. The {@link 839 * #getElementOpenType()} method of the returned 840 * {@code ArrayType} returns the {@link SimpleType} 841 * corresponding to the wrapper type of the primitive 842 * type of the array. 843 * 844 * @throws IllegalArgumentException if <var>arrayClass</var> is not 845 * a primitive array. 846 * 847 * @since 1.6 848 */ 849 @SuppressWarnings("unchecked") // can't get appropriate T for primitive array 850 public static <T> ArrayType<T> getPrimitiveArrayType(Class<T> arrayClass) { 851 // Check if the supplied parameter is an array 852 // 853 if (!arrayClass.isArray()) { 854 throw new IllegalArgumentException("arrayClass must be an array"); 855 } 856 857 // Calculate array dimension and component type name 858 // 859 int n = 1; 860 Class<?> componentType = arrayClass.getComponentType(); 861 while (componentType.isArray()) { 862 n++; 863 componentType = componentType.getComponentType(); 864 } 865 String componentTypeName = componentType.getName(); 866 867 // Check if the array's component type is a primitive type 868 // 869 if (!componentType.isPrimitive()) { 870 throw new IllegalArgumentException( 871 "component type of the array must be a primitive type"); 872 } 873 874 // Map component type name to corresponding SimpleType 875 // 876 final SimpleType<?> simpleType = 877 getPrimitiveOpenType(componentTypeName); 878 879 // Build primitive array 880 // 881 try { 882 @SuppressWarnings("rawtypes") 883 ArrayType at = new ArrayType(simpleType, true); 884 if (n > 1) 885 at = new ArrayType<T>(n - 1, at); 886 return at; 887 } catch (OpenDataException e) { 888 throw new IllegalArgumentException(e); // should not happen 889 } 890 } 891 892 /** 893 * Replace/resolve the object read from the stream before it is returned 894 * to the caller. 895 * 896 * @serialData The new serial form of this class defines a new serializable 897 * {@code boolean} field {@code primitiveArray}. In order to guarantee the 898 * interoperability with previous versions of this class the new serial 899 * form must continue to refer to primitive wrapper types even when the 900 * {@code ArrayType} instance describes a primitive type array. So when 901 * {@code primitiveArray} is {@code true} the {@code className}, 902 * {@code typeName} and {@code description} serializable fields 903 * are converted into primitive types before the deserialized 904 * {@code ArrayType} instance is return to the caller. The 905 * {@code elementType} field always returns the {@code SimpleType} 906 * corresponding to the primitive wrapper type of the array's 907 * primitive type. 908 * <p> 909 * Therefore the following serializable fields are deserialized as follows: 910 * <ul> 911 * <li>if {@code primitiveArray} is {@code true} the {@code className} 912 * field is deserialized by replacing the array's component primitive 913 * wrapper type by the corresponding array's component primitive type, 914 * e.g. {@code "[[Ljava.lang.Integer;"} will be deserialized as 915 * {@code "[[I"}.</li> 916 * <li>if {@code primitiveArray} is {@code true} the {@code typeName} 917 * field is deserialized by replacing the array's component primitive 918 * wrapper type by the corresponding array's component primitive type, 919 * e.g. {@code "[[Ljava.lang.Integer;"} will be deserialized as 920 * {@code "[[I"}.</li> 921 * <li>if {@code primitiveArray} is {@code true} the {@code description} 922 * field is deserialized by replacing the array's component primitive 923 * wrapper type by the corresponding array's component primitive type, 924 * e.g. {@code "2-dimension array of java.lang.Integer"} will be 925 * deserialized as {@code "2-dimension array of int"}.</li> 926 * </ul> 927 * 928 * @since 1.6 929 */ 930 private Object readResolve() throws ObjectStreamException { 931 if (primitiveArray) { 932 return convertFromWrapperToPrimitiveTypes(); 933 } else { 934 return this; 935 } 936 } 937 938 private <T> ArrayType<T> convertFromWrapperToPrimitiveTypes() { 939 String cn = getClassName(); 940 String tn = getTypeName(); 941 String d = getDescription(); 942 for (Object[] typeDescr : PRIMITIVE_ARRAY_TYPES) { 943 if (cn.indexOf((String)typeDescr[PRIMITIVE_WRAPPER_NAME_INDEX]) != -1) { 944 cn = cn.replaceFirst( 945 "L" + typeDescr[PRIMITIVE_WRAPPER_NAME_INDEX] + ";", 946 (String) typeDescr[PRIMITIVE_TYPE_KEY_INDEX]); 947 tn = tn.replaceFirst( 948 "L" + typeDescr[PRIMITIVE_WRAPPER_NAME_INDEX] + ";", 949 (String) typeDescr[PRIMITIVE_TYPE_KEY_INDEX]); 950 d = d.replaceFirst( 951 (String) typeDescr[PRIMITIVE_WRAPPER_NAME_INDEX], 952 (String) typeDescr[PRIMITIVE_TYPE_NAME_INDEX]); 953 break; 954 } 955 } 956 return new ArrayType<T>(cn, tn, d, 957 dimension, elementType, primitiveArray); 958 } 959 960 /** 961 * Nominate a replacement for this object in the stream before the object 962 * is written. 963 * 964 * @serialData The new serial form of this class defines a new serializable 965 * {@code boolean} field {@code primitiveArray}. In order to guarantee the 966 * interoperability with previous versions of this class the new serial 967 * form must continue to refer to primitive wrapper types even when the 968 * {@code ArrayType} instance describes a primitive type array. So when 969 * {@code primitiveArray} is {@code true} the {@code className}, 970 * {@code typeName} and {@code description} serializable fields 971 * are converted into wrapper types before the serialized 972 * {@code ArrayType} instance is written to the stream. The 973 * {@code elementType} field always returns the {@code SimpleType} 974 * corresponding to the primitive wrapper type of the array's 975 * primitive type. 976 * <p> 977 * Therefore the following serializable fields are serialized as follows: 978 * <ul> 979 * <li>if {@code primitiveArray} is {@code true} the {@code className} 980 * field is serialized by replacing the array's component primitive 981 * type by the corresponding array's component primitive wrapper type, 982 * e.g. {@code "[[I"} will be serialized as 983 * {@code "[[Ljava.lang.Integer;"}.</li> 984 * <li>if {@code primitiveArray} is {@code true} the {@code typeName} 985 * field is serialized by replacing the array's component primitive 986 * type by the corresponding array's component primitive wrapper type, 987 * e.g. {@code "[[I"} will be serialized as 988 * {@code "[[Ljava.lang.Integer;"}.</li> 989 * <li>if {@code primitiveArray} is {@code true} the {@code description} 990 * field is serialized by replacing the array's component primitive 991 * type by the corresponding array's component primitive wrapper type, 992 * e.g. {@code "2-dimension array of int"} will be serialized as 993 * {@code "2-dimension array of java.lang.Integer"}.</li> 994 * </ul> 995 * 996 * @since 1.6 997 */ 998 private Object writeReplace() throws ObjectStreamException { 999 if (primitiveArray) { 1000 return convertFromPrimitiveToWrapperTypes(); 1001 } else { 1002 return this; 1003 } 1004 } 1005 1006 private <T> ArrayType<T> convertFromPrimitiveToWrapperTypes() { 1007 String cn = getClassName(); 1008 String tn = getTypeName(); 1009 String d = getDescription(); 1010 for (Object[] typeDescr : PRIMITIVE_ARRAY_TYPES) { 1011 if (cn.indexOf((String) typeDescr[PRIMITIVE_TYPE_KEY_INDEX]) != -1) { 1012 cn = cn.replaceFirst( 1013 (String) typeDescr[PRIMITIVE_TYPE_KEY_INDEX], 1014 "L" + typeDescr[PRIMITIVE_WRAPPER_NAME_INDEX] + ";"); 1015 tn = tn.replaceFirst( 1016 (String) typeDescr[PRIMITIVE_TYPE_KEY_INDEX], 1017 "L" + typeDescr[PRIMITIVE_WRAPPER_NAME_INDEX] + ";"); 1018 d = d.replaceFirst( 1019 (String) typeDescr[PRIMITIVE_TYPE_NAME_INDEX], 1020 (String) typeDescr[PRIMITIVE_WRAPPER_NAME_INDEX]); 1021 break; 1022 } 1023 } 1024 return new ArrayType<T>(cn, tn, d, 1025 dimension, elementType, primitiveArray); 1026 } 1027 }